/******************************************************************************
//             INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in accordance with the terms of that agreement.
//    Copyright(c) 2000 - 2001 Intel Corporation. All Rights Reserved.
//
//  VSS: 
//     $Workfile: ijehufp.c $
//     $Revision: 1 $
//     $Date: 4/29/01 4:03p $
//     $Archive: /ipp_sw/mainline/src/ansi_c/image/jpeg/ijehufp.c $
//
//  Description:
//      Intel(R) Integrated Performance Primitives - JPEG Helper file.
//      This file contains Huffman encoding (Progressive) functions.
//
//  History:
//     Date         Author        Changes
//     2001/1/20   Jerry Hu       Created (Version 0.2 Progressive)
//
********************************************************************************/
#include "_ippjpeg.h"
#include "ijehufsh.h"

/******************************************************************************
// Name:             _AppendBits
// Description:      Append bits into stream.
// Input Arguments:  
//    pSrcBitBuffer: Identifies bits buffer which size shall be larger than 256+1024;
//       srcBitsLen: Identifies the current bits length in pSrcBitBuffer.
//
// Output Arguments:
//       pDstBuffer: Identifies output stream buffer.
//          pDstLen: Specifies the bits length that has been used 
//                   the stream buffer.
//                  
// Returns:          
//                   none.
//                  
******************************************************************************/
static void _AppendBits(const Ipp8u  *pSrcBitBuffer, 
                         int    srcBitsLen, 
                         Ipp8u  *pDstBuffer, 
                         int    *pDstBitsLen)
{
    int i;
    for (i=srcBitsLen; i>0; i--) {
        _IppWriteStreamBits(*pSrcBitBuffer++, 1, 
                         pDstBuffer, pDstBitsLen);
    }
}

/******************************************************************************
// Name:             _EncodeEOBRUN
// Description:      Encode EOB run length.
// Input Arguments:  
//           eobRun: Indicates EOB run length.
//            ppDst: Identifies output stream buffer
//      pDstBitsLen: Specifies the bits length that has been used 
//                   the stream buffer.
//        pHuffSize: Identifies huffman size buffer.
//        pHuffCode: Identifies huffman code buffer.
//
// Output Arguments:
//            ppDst: Identifies output stream buffer
//      pDstBitsLen: Identifies the updated bits length that has been used 
//                   the stream buffer.
//                  
// Returns:          
//                   none.
//                  
******************************************************************************/
static void _EncodeEOBRUN( int    eobRun,
                           Ipp8u  *pDst,  
                           int    *pDstBitsLen,
                           const Ipp8u  *pHuffSize,
                           const Ipp16u *pHuffCode)
{
    int    rs;
    int    rrrr;
    int    val;
    int    nbits;

    _EOBSIZE(rrrr, eobRun);

    rs    = rrrr<<4;
    val   = pHuffCode[rs];
    nbits = pHuffSize[rs];

    _IppWriteStreamBits(val, nbits, pDst, pDstBitsLen);
    _IppWriteStreamBits(eobRun, rrrr, pDst, pDstBitsLen);
} 

/******************************************************************************
// Name:             ippiEncodeHuffman_Direct_JPEG_DCFirst_16s1u
// Description:      Encods DC coefficient for the first scan.
// Input Arguments: 
//             pSrc: Identifies source coefficient data in 8x8.
//      pDstBitsLen: Indicates the number of bits that have been Encoded before.
//          pDCPred: Identifies the previous DC value of prediction.
//              al:  Indiactes successive approximation bit position low (for point tranform)
//     pDCHuffTable: Identifies "IppJPEGDeHuffTable" data structure for DC decoding.
//
// Output Arguments:
//             pDst: Identifies source huffman stream data.
//      pDstBitsLen: Indicates the number of bits that have been Encoded after this function calling.
//          pDCPred: Identifies the current DC value of prediction.
//
// Remarks:      
//                   1. The start address of "pSrc" shall be aligned at 4byte boundary.
//                   2. It is better to do this alignment at 32byte boundary to optimize 
//                      D-Cache accessing.
//                   3. *pDCPred shall be 0 in initial or after each restart interval.
//
// Returns:         
//    IPP_STATUS_OK: Succeeds.
// IPP_STATUS_ERROR: Encoding error.
******************************************************************************/
IPPFUN(IppStatus, ippiEncodeHuffman_Direct_JPEG_DCFirst_16s1u)(const Ipp16s  *pSrc,
                                                              Ipp8u         *pDst,  
                                                              int           *pDstBitsLen,
                                                              Ipp16s        *pDCPred,
                                                              int           al,
                                                              const IppJPEGEnHuffTable 
                                                                      *pDCHuffTable)
{
    int    data;
    int    ssss;
    int    val;
    int    nbits;
    Ipp16s value;

    _IPP_CHECK_ARG((NULL!=pSrc)&&(NULL!=pDst));
    _IPP_CHECK_ARG(_IPP_CHECK_ALIGN_4BYTE(pSrc));
    _IPP_CHECK_ARG((NULL!=pDstBitsLen)&&(0<=*pDstBitsLen));
    _IPP_CHECK_ARG(NULL!=pDCPred);
    _IPP_CHECK_ARG(0<=al);
    _IPP_CHECK_ARG(NULL!=pDCHuffTable);


    /*
    // Encode DC Coefficient
    */
    value = (Ipp16s)(pSrc[0]>>al);
    data  = value - *pDCPred;
    *pDCPred = value;

    _CSIZE(ssss, data);

    /* Encode Size of DC Coefficient */
    val   = pDCHuffTable->huffCode[ssss];
    nbits = pDCHuffTable->huffSize[ssss];

    _IppWriteStreamBits(val, nbits, pDst, pDstBitsLen);

    /* Encode DC Coefficient Value */
    _IppWriteStreamBits(data,ssss, pDst, pDstBitsLen);

    return IPP_STATUS_OK;
}

/******************************************************************************
// Name:             ippiEncodeHuffman_Direct_JPEG_DCRefine_16s1u
// Description:      Encods DC coefficient for the refine scan.
// Input Arguments: 
//             pSrc: Identifies source coefficient data in 8x8.
//      pDstBitsLen: Indicates the number of bits that have been Encoded before.
//              al:  Indiactes successive approximation bit position low (for point tranform)
//
// Output Arguments:
//             pDst: Identifies source huffman stream data.
//      pDstBitsLen: Indicates the number of bits that have been Encoded after this function calling.
//
// Remarks:      
//                   1. The start address of "pSrc" shall be aligned at 4byte boundary.
//                   2. It is better to do this alignment at 32byte boundary to optimize 
//                      D-Cache accessing.
//
// Returns:         
//    IPP_STATUS_OK: Succeeds.
// IPP_STATUS_ERROR: Encoding error.
******************************************************************************/
IPPFUN(IppStatus, ippiEncodeHuffman_Direct_JPEG_DCRefine_16s1u) (const Ipp16s  *pSrc,
                                                                Ipp8u         *pDst,  
                                                                int           *pDstBitsLen,
                                                                int           al)
{
    int data;

    _IPP_CHECK_ARG((NULL!=pSrc)&&(NULL!=pDst));
    _IPP_CHECK_ARG(_IPP_CHECK_ALIGN_4BYTE(pSrc));
    _IPP_CHECK_ARG((NULL!=pDstBitsLen)&&(0<=*pDstBitsLen));
    _IPP_CHECK_ARG(0<=al);

    data =  pSrc[0]>>al;

    _IppWriteStreamBits(data, 1, pDst, pDstBitsLen);
    
    return IPP_STATUS_OK;
}

/******************************************************************************
// Name:             ippiEncodeHuffman_Direct_JPEG_ACFirst_16s1u
// Description:      Encods AC coefficients for the first scan.
// Input Arguments: 
//             pSrc: Identifies source coefficient data in 8x8.
//      pDstBitsLen: Indicates the number of bits that have been Encoded before.
//          pEOBRun: Identifies EOB run length in last time.
//               ss: Indicates start of selection.
//               se: Indicates end of selection.
//               al: Indiactes successive approximation bit position low (for point tranform)
//     pACHuffTable: Identifies "IppJPEGDeHuffTable" data structure for AC decoding.
//
// Output Arguments:
//             pDst: Identifies source huffman stream data.
//      pDstBitsLen: Indicates the number of bits that have been Encoded after this function calling.
//          pEOBRun: Identifies EOB run length in current time.
//
// Remarks:      
//                   1. The start address of "pDst" shall be aligned at 4byte boundary.
//                   2. It is better to do this alignment at 32byte boundary to optimize 
//                      D-Cache accessing.
//
// Returns:         
//    IPP_STATUS_OK: Succeeds.
// IPP_STATUS_ERROR: Encoding error.
******************************************************************************/
IPPFUN(IppStatus, ippiEncodeHuffman_Direct_JPEG_ACFirst_16s1u) (const Ipp16s *pSrc,
                                                               Ipp8u        *pDst,  
                                                               int          *pDstBitsLen,
                                                               int          *pEOBRun,
                                                               int          ss,
                                                               int          se,
                                                               int          al,
                                                               const IppJPEGEnHuffTable 
                                                                            *pACHuffTable)
{
    int    rrrr;
    int    ssss;
    int    rs;
    int    data;
    int    ptdata;
    int    k;
    int    val;
    int    nbits;
    const Ipp8u  *pHuffSize;
    const Ipp16u *pHuffCode;

    _IPP_CHECK_ARG((NULL!=pSrc)&&(NULL!=pDst));
    _IPP_CHECK_ARG(_IPP_CHECK_ALIGN_4BYTE(pSrc));
    _IPP_CHECK_ARG((NULL!=pDstBitsLen)&&(0<=*pDstBitsLen));
    _IPP_CHECK_ARG((NULL!=pEOBRun)&&(0<=*pEOBRun));
    _IPP_CHECK_ARG((0<=al)&&(0<ss)&&(ss<=se)&&(64>se));
    _IPP_CHECK_ARG(NULL!=pACHuffTable);


    /*
    // Encode AC Coefficient(s)
    */

    rrrr = 0;
    pHuffCode=pACHuffTable->huffCode;
    pHuffSize=pACHuffTable->huffSize;

    for (k=ss; k<=se; k++) {
        data = pSrc[_nIppIZigZagIndex[k]];
        if (0==data) {
           rrrr++; /* Increment run-length of zeroes */
           continue;
        } 
        /*
        // Point trandorm by al
        */
        if (0>data) {
            data=-data;
            data>>=al;
            ptdata=~data;
        } else {
            data>>=al;
            ptdata=data;
        }

        if (0==data) {
           rrrr++; /* Increment run-length of zeroes */
           continue;
        } 

        if (0<(*pEOBRun)) {
            _EncodeEOBRUN((*pEOBRun),
                          pDst,  
                          pDstBitsLen,
                          pHuffSize,
                          pHuffCode);
            (*pEOBRun)=0;
        }

        // if run length > 15, must emit special run-length-16 codes (0xf0)
        while(rrrr > 15) {
            val   = pHuffCode[0xf0];
            nbits = pHuffSize[0xf0];
            /* Encode zeros */
            _IppWriteStreamBits(val, nbits, pDst, pDstBitsLen);
            rrrr-=16;
        }
    
        /* Encode ZZ(K) */
        _CSIZE(ssss, data);
        rs=(rrrr<<4)+ssss;
        val   = pHuffCode[rs];
        nbits = pHuffSize[rs];
        _IppWriteStreamBits(val, nbits, pDst, pDstBitsLen);
        _IppWriteStreamBits(ptdata, ssss, pDst, pDstBitsLen);
        rrrr = 0;
    }
  
    if (rrrr>0) {
        (*pEOBRun)++;
        if (0x7fff<=(*pEOBRun)) {
            _EncodeEOBRUN((*pEOBRun),
                          pDst,  
                          pDstBitsLen,
                          pHuffSize,
                          pHuffCode);
            (*pEOBRun)=0;
        }
    }

    return IPP_STATUS_OK;
}

/******************************************************************************
// Name:             ippiEncodeHuffman_Direct_JPEG_ACRefine_16s1u
// Description:      Encods AC coefficients for the first scan.
// Input Arguments: 
//             pSrc: Identifies source coefficient data in 8x8.
//      pDstBitsLen: Indicates the number of bits that have been Encoded before.
//          pEOBRun: Identifies EOB run length in last time.
//               ss: Indicates start of selection.
//               se: Indicates end of selection.
//               al: Indiactes successive approximation bit position low (for point tranform)
//       pBitBuffer: Identifies bits buffer which size shall be larger than 256+1024+31;
//           pBELen: Identifies the current bits length in pBitBuffer.
//     pACHuffTable: Identifies "IppJPEGDeHuffTable" data structure for AC decoding.
//
// Output Arguments:
//             pDst: Identifies source huffman stream data.
//      pDstBitsLen: Indicates the number of bits that have been Encoded after this function calling.
//          pEOBRun: Identifies EOB run length in current time.
//       pBitBuffer: Identifies bits buffer which size shall be larger than 256+1024+31;
//           pBELen: Identifies the current bits length in pBitBuffer.
//
// Remarks:      
//                   1. The start address of "pSrc" and "pBitBuffer"shall be aligned at 4byte boundary.
//                   2. It is better to do this alignment at 32byte boundary to optimize 
//                      D-Cache accessing.
//
// Returns:         
//    IPP_STATUS_OK: Succeeds.
// IPP_STATUS_ERROR: Encoding error.
******************************************************************************/
IPPFUN(IppStatus, ippiEncodeHuffman_Direct_JPEG_ACRefine_16s1u)( const Ipp16s *pSrc,
                                                                Ipp8u        *pDst,  
                                                                int          *pDstBitsLen,
                                                                int          *pEOBRun,
                                                                Ipp8u        *pBitBuffer,
                                                                int          *pBELen,
                                                                int          ss,
                                                                int          se,
                                                                int          al,
                                                                const IppJPEGEnHuffTable 
                                                                             *pACHuffTable)

{
    int    k;
    int    rrrr, rs;
    int    value;
    int    EOB;
    int    BE, BR;
    int    val, nbits;
    int    *pTempBuf;
    Ipp8u  *pBEBuffer;
    Ipp8u  *pBRBuffer;
    Ipp8u  *pAlginBuf;
    const Ipp8u  *pHuffSize;
    const Ipp16u *pHuffCode;

    _IPP_CHECK_ARG((NULL!=pSrc)&&(NULL!=pDst));
    _IPP_CHECK_ARG(_IPP_CHECK_ALIGN_4BYTE(pSrc));
    _IPP_CHECK_ARG((NULL!=pDstBitsLen)&&(0<=*pDstBitsLen));
    _IPP_CHECK_ARG((NULL!=pEOBRun)&&(0<=*pEOBRun));
    _IPP_CHECK_ARG(NULL!=pBitBuffer);
    _IPP_CHECK_ARG((NULL!=pBELen)&&(0<=*pBELen));
    _IPP_CHECK_ARG((0<=al)&&(0<ss)&&(ss<=se)&&(64>se));
    _IPP_CHECK_ARG(NULL!=pACHuffTable);

    BR   = 0;
    BE   = *pBELen;
    EOB  = 0;
    rrrr = 0;

    pAlginBuf = (Ipp8u *)_IPP_32BYTEALIGN(pBitBuffer);
    pTempBuf  = (int *)pAlginBuf;
    pBEBuffer = &pAlginBuf[256];
    pBRBuffer = &pBEBuffer[BE];
    pHuffCode=pACHuffTable->huffCode;
    pHuffSize=pACHuffTable->huffSize;
  
    /* 
    // 1. Find EOB 
    // 2. Apply point transform by al
    */
    for (k = ss; k <= se; k++) {
        value = pSrc[_nIppIZigZagIndex[k]];

        /* value is abs value of input */
        if (value < 0) {
           value = -value;
        }

        /* apply the point transform */
        value >>= al;
        pTempBuf[k] = value;

        /* EOB = index of last newly-nonzero coef */
        if (1==value) {
           EOB = k;
        }
    }

    /*
    // Encode the AC coefficients per section G.1.2.3, Figure G.7
    */
    for (k = ss; k <= se; k++) {
        value = pTempBuf[k];

        if (0==value) {
           rrrr++;
           continue;
        }

        /* 
        // Process Zero run 
        // Figure G.7 in T.81 cause a error, 
        // k should be greater than EOB
        */
        while ((rrrr>15)&&(EOB>=k)) {
            if (0<(*pEOBRun)) {
                _EncodeEOBRUN((*pEOBRun),
                              pDst,  
                              pDstBitsLen,
                              pHuffSize,
                              pHuffCode);
                (*pEOBRun)=0;
            }

            if (0<BE) {
                /* append BE bits */
                _AppendBits(pBEBuffer, 
                            BE, 
                            pDst,  
                            pDstBitsLen);
                BE=0;
            }            
          
            /* Encode ZRL */
            val   = pHuffCode[0xf0];
            nbits = pHuffSize[0xf0];
            _IppWriteStreamBits(val, nbits, pDst, pDstBitsLen);
            rrrr-=16;
            
            if (0<BR) {
                /* append BE bits */
                _AppendBits(pBRBuffer, 
                            BR, 
                            pDst,  
                            pDstBitsLen);
                BR=0;
            }
            pBRBuffer=pBEBuffer;
        }

        if (1<value) {
           /* Append correction bit */
           pBRBuffer[BR] = (Ipp8u)(value & 1);
           BR++;
           continue;
        }

        /*
        // Processing zero history value
        */
        if (0<(*pEOBRun)) {
            _EncodeEOBRUN((*pEOBRun),
                          pDst,  
                          pDstBitsLen,
                          pHuffSize,
                          pHuffCode);
            (*pEOBRun)=0;
        }

        if (0<BE) {
            /* append BE bits */
            _AppendBits(pBEBuffer, 
                        BE, 
                        pDst,  
                        pDstBitsLen);
            BE=0;
        }            
          
        /* Encoding zero history value according to rule a */
        rs    = (rrrr<<4)+1;
        val   = pHuffCode[rs];
        nbits = pHuffSize[rs];
        _IppWriteStreamBits(val, nbits, pDst, pDstBitsLen);
        value = (pSrc[_nIppIZigZagIndex[k]]<0)?0:1;
        _IppWriteStreamBits(value, 1, pDst, pDstBitsLen);

        if (0<BR) {
            /* append BE bits */
            _AppendBits(pBRBuffer, 
                        BR, 
                        pDst,  
                        pDstBitsLen);
            BR=0;
        }
        pBRBuffer=pBEBuffer;
        /* reset ZRL */
        rrrr=0;
    }

    if ((0<rrrr)||(0<BR)) {
        (*pEOBRun)++;
        BE += BR;
        /*
        // Buffer length for the buffered bits is 1024
        // EOBRun is used to process the overflow for the buffer
        */
        if ((0x7FFF<=(*pEOBRun))||(BE>(_MAXCORRBITS-63))) {
            _EncodeEOBRUN((*pEOBRun),
                          pDst,  
                          pDstBitsLen,
                          pHuffSize,
                          pHuffCode);
            (*pEOBRun)=0;
            /* append BE bits */
            _AppendBits(pBEBuffer, 
                        BE, 
                        pDst,  
                        pDstBitsLen);
            BE=0;
        }
    }

    *pBELen=BE;

    return IPP_STATUS_OK;
}

/******************************************************************************
// Name:             ippiEncodeHuffman_JPEG_ACFinal
// Description:      Encods AC coefficients for the first scan.
// Input Arguments: 
//        srcEOBRun: Indicates EOB run length.
//       pBitBuffer: Identifies bits buffer which size shall be larger than 256+1024+31;
//           pBELen: Identifies the current bits length in pBitBuffer.
//     pACHuffTable: Identifies "IppJPEGDeHuffTable" data structure for AC decoding.
//
// Output Arguments:
//             pDst: Identifies source huffman stream data.
//      pDstBitsLen: Indicates the number of bits that have been Encoded after this function calling.
//
// Remarks:      
//                   1. The start address of "pSrc" and "pBitBuffer"shall be aligned at 4byte boundary.
//                   2. It is better to do this alignment at 32byte boundary to optimize 
//                      D-Cache accessing.
//
// Returns:         
//    IPP_STATUS_OK: Succeeds.
// IPP_STATUS_ERROR: Encoding error.
******************************************************************************/
IPPFUN(IppStatus, ippiEncodeHuffman_Direct_JPEG_ACFinal) (int          srcEOBRun,
                                                         const Ipp8u  *pBitBuffer,
                                                         int          nBELen,
                                                         Ipp8u        *pDst,  
                                                         int          *pDstBitsLen,
                                                         const IppJPEGEnHuffTable 
                                                                      *pACHuffTable)
{
    _IPP_CHECK_ARG(0<=srcEOBRun);
    _IPP_CHECK_ARG(0<=nBELen);
    _IPP_CHECK_ARG(NULL!=pDst);
    _IPP_CHECK_ARG((NULL!=pDstBitsLen)&&(0<=*pDstBitsLen));
    _IPP_CHECK_ARG(NULL!=pACHuffTable);

    if (0<srcEOBRun) {
        _EncodeEOBRUN(srcEOBRun,
                      pDst,  
                      pDstBitsLen,
                      pACHuffTable->huffSize,
                      pACHuffTable->huffCode);
    }

    if (0<nBELen) {
        /* append BE bits */
        Ipp8u  *pAlginBuf=(Ipp8u *)_IPP_32BYTEALIGN(pBitBuffer);
        _AppendBits(&pAlginBuf[256], 
                    nBELen, 
                    pDst,  
                    pDstBitsLen);
    }

    return IPP_STATUS_OK;
}

/* EOF */
